package cate.game.pvp.guildwar.data;

import cate.common.table.d.GDFight;
import cate.common.table.d.GDFunc;
import cate.common.table.d.GDGuildWar;
import cate.common.table.d.GDOperation;
import cate.common.table.pvp.guildwar.row.GuildWarBuffRow;
import cate.common.table.pvp.guildwar.row.GuildWarDifficultyRow;
import cate.common.table.pvp.guildwar.row.GuildWarPtRow;
import cate.common.table.pvp.guildwar.row.GuildWarRankRow;
import cate.common.util.TimeTool;
import cate.game.attr.FightAttr;
import cate.game.mail.po.MailBuilder;
import cate.game.play.param.FightParam;
import cate.game.play.part.FightSide;
import cate.game.play.part.PlaySide;
import cate.game.play.proce.play.PlayPO;
import cate.game.play.vo.ReplayUtils;
import cate.game.pvp.guildwar.GuildWarFTC;
import cate.game.pvp.guildwar.data.msg.GWPtInnerUpdate;
import cate.game.pvp.guildwar.data.msg.GWSideInnerUpdate;
import cate.game.res.MixRes;
import cate.game.role.Role;
import cate.game.role.mail.Mail;
import cate.game.role.pvp.guildwar.msg.GuildWarBaseResp;
import cate.game.role.pvp.guildwar.msg.GuildWarFieldResp;
import cate.game.role.pvp.guildwar.msg.GuildWarPtResp;
import cate.game.role.res.guild.GuildFuncBase;
import cate.game.role.res.guild.po.GuildPO;
import cate.game.role.res.guild.po.member.GuildMemberDetailPO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.mysql.cj.util.TimeUtil;
import cp.solution.manage.api.open.replay.ReplaySaveReq;
import cp.solution.manage.api.open.replay.ReplaySaveResp;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;


@NoteClass("公会战-公会数据")
@Slf4j
public class GuildWar extends GuildFuncBase {

	@NoteField("总场次")
	public int total;

	@NoteField("总胜利场次")
	public int totalWin;

	@NoteField("赛季积分")
	public int point;

	@NoteField("连胜次数")
	public int winningSteak;

	@NoteField(value = "排名",detail = "非实时")
	public int rank;

	@NoteField("赛季参赛次数")
	public int seasonNum;

	@NoteField("匹配成功")
	public boolean match;

	@NoteField("同步积分成功")
	public boolean synPoint;

	@NoteField("战场数据")
	@JsonIgnore
	public GWBattleField field;

	@Override
	public void initialize(GuildPO parent) {
		super.initialize(parent);
		if (field == null) {
			field = new GWBattleField();
		}
		field.initialize(this);
		if (point < GDGuildWar.INIT_POINT) {
			point = GDGuildWar.INIT_POINT;
		}
		if (rank == 0) {
			rank = -1;
		}
	}


	public void onSeasonChange() {
		if (seasonNum > 0) {
			GuildWarRankRow row = getGame().table.guildWar.rank.findRank(point);
			if (row != null) {
				for (GuildMemberDetailPO member : funcDependent.getGuild().member.getCopyMembers()) {
					List<Object> params = new ArrayList<>();
					params.add(row.rankName);
					Mail mail = new MailBuilder().setContentId(row.mailTid).setParamList(params).
							setReward(new MixRes(row.seasonRwdStr)).setOperation(GDOperation.GUILD_WAR_SEASON_RANK).build();
					getGame().mail.send(member.it.uid, mail);
				}
			}
		}
		setPoint((int) (point * 0.7d));
		updateRank(-1);
		seasonNum = 0;
	}

	public void setPoint(int point){
		this.point = Math.max(GDGuildWar.INIT_POINT, point);
	}

	@Override
	public int getFuncId() {
		return GDFunc.GuildFunc.WAR;
	}

	//匹配成功
	public boolean hasMatch() {
		return match;
	}

	//轮空
	public boolean pass() {
		return field.b.isEmpty();
	}

	//战斗数据准备就绪
	public boolean fightDataReady() {
		return !field.a.ptMap.isEmpty();
	}

	public void onDaySpan() {
		match = false;
		synPoint = false;
		field.clear();
	}


	public void onPeriodChange(byte period) {
		switch (period) {
			case GDGuildWar.Period.FIGHT:
				//构建机器人据点
				buildRobotPt();
				// 通知玩家最新的信息
				noticeOnlineBase();
				break;
			case GDGuildWar.Period.SETTLE_RWD:
				calFightResult();
				trySynPoint();
				noticeOnlineBase();
				break;
			default:
				break;
		}
	}

	public void updateRank(int rank) {
		if (this.rank == rank) {
			return;
		}
		this.rank = rank;
		noticeOnlineBase();
	}

	public void trySynPoint() {
		boolean success = getGame().pvp.guildWar.getMessagingModule().recordGuildRank(funcDependent.guild);
		if (success) {
			this.synPoint = true;
		}
	}

	public synchronized EcResult<Void> challenge(Role role, int ptId, int difficulty, int star) {
		EcResult<Void> r = new EcResult();
		GuildWarPt pt = field.a.ptMap.get(ptId);
		if (pt == null) {
			return r.fail("据点不存在");
		}
		if (pt.star != star) {
			role.send(new GuildWarFieldResp(GDFight.Team.A, this));
			role.sendNow(new GuildWarPtResp(GDFight.Team.A, pt));
			return r.fail("据点数据已更新,请重新打开界面");
		}
		GuildWarPtRow ptRow = role.getGame().table.guildWar.pt.findPt(field.b.profile.level, ptId);
		if (ptRow == null) {
			return r.fail("据点配置不存在");
		}
		// 挑战废墟只能选择中等难度
		boolean isRuin = pt.star == GDGuildWar.STAR_MAX;
		if (isRuin) {
			difficulty = 2;
		}
		GuildWarFTC context = new GuildWarFTC(role, difficulty, ptRow);
		context.funcId = GDFunc.GuildFunc.WAR;
		context.setPositionApply(GDFunc.ADVENTURE);
		context.param = getFightParam(difficulty);
		FightSide sideB = role.getGame().fight.side.createFightSideByPlaySide(GDFight.Team.B, pt.ps.copy());
		EcResult<PlayPO> r_fp = role.getGame().play.startByRole(role, context, sideB);
		if (!r_fp.ok()) {
			return r.fail(r_fp.message);
		}
		PlayPO playPO = r_fp.data;
		boolean win = playPO.winner == GDFight.Team.A;
		field.addRoleUid(role.getUid());
		//记录日志
		GuildWarLog guildWarLog = new GuildWarLog();
		guildWarLog.a = playPO.a.snapshot.base.copy();
		guildWarLog.b = playPO.b.snapshot.base.copy();
		guildWarLog.win = win;
		try {
			ReplaySaveReq saveReq = ReplayUtils.toReplaySaveReq(playPO, role.getGame().time.now() + TimeTool.TimeCons.DAY);
			EcResult<ReplaySaveResp> rsp = role.getGame().openApi().saveReplay(saveReq);
			if (!rsp.ok()) {
				log.error("公会战保存录像错误 {}", rsp.message);
			} else {
				guildWarLog.replayUid = rsp.data.getId();
			}
		} catch (Exception e) {
			log.error("", e);
		}
		if (win) {
			if (isRuin) {
				field.a.addBuffLevel(role.getGame());
			} else {
				guildWarLog.star = pt.addStar(difficulty);
			}
		}
		field.addLog(guildWarLog.copy());
		role.getPvp().guildWar.addLog(guildWarLog.copy());
		if (win) {
			// 通知所有人本次挑战的更新信息
			funcDependent.getGuild().noticeOnline(m -> new GuildWarFieldResp(GDFight.Team.A, this).sendNow(m));
		}
		// 通知对方公会本次挑战的更新信息
		role.getGame().pvp.guildWar.getDataManager().sendSideUpdate(GWSideInnerUpdate.of(this, GWPtInnerUpdate.of(pt), guildWarLog));
		return r.success();
	}

	private void calFightResult() {
		int add = 0;
		boolean win = field.isWin();
		if (win) {
			field.winner = GDFight.Team.A;
			add = GDGuildWar.WIN_BASE_POINT;
			if (!pass()) {
				int diff = (int) ((field.b.profile.point - field.a.profile.point) * GDGuildWar.POINT_DIFFER_COEFF);
				diff = Math.max(0, diff);
				diff = Math.min(GDGuildWar.POINT_DIFFER_MAX, diff);
				add += diff;
			}
		} else {
			field.winner = GDFight.Team.B;
			add -= GDGuildWar.FAIL_POINT;
		}
		total++;
		setPoint(this.point + add);
		if (win) {
			totalWin++;
			winningSteak++;
			getGame().table.guildWar.treasure.findItems(getGame().getOpenDay()).
					forEach(row -> getFuncDependent().getGuild().treasureHouse.putTreasureIn(null, row.itemId, row.num, GDFunc.GuildFunc.WAR));

		} else {
			winningSteak = 0;
		}
	}

	public void onMatch(GWGuildProfile chose, GWGuildProfile enemy) {
		match = true;
		// 首先初始化我方的数据
		// 基础信息记录
		field.a.profile = chose.copy();
		if (enemy != null) {
			// 敌方据点信息更新
			field.b.profile = enemy.copy();
		}
		// 构建据点(给对方打的)
		buildPt();
		noticeOnlineBase();
		seasonNum++;
	}

	public void handleOtherSide(GWSideInnerUpdate data){
		field.updateOtherSide(data);
	}

	private void buildPt() {
		List<GuildMemberDetailPO> members = funcDependent.guild.member.getCopyMembers();
		List<PlaySide> playSides = new ArrayList<>();
		for (GuildMemberDetailPO m : members.stream().sorted(Comparator.comparingLong(a -> -a.it.base.power)).collect(Collectors.toList())) {
			if (playSides.size() >= GDGuildWar.PT_MAX_NUM) {
				break;
			}
			Role role = getGame().role.getRole(m.it.uid, true);
			if (role != null) {
				EcResult<PlaySide> r = getGame().fight.side.createPlaySideByRole(role, GDFunc.ADVENTURE);
				if (r.ok()) {
					playSides.add(r.data);
				} else {
					log.error("公会战构建阵容异常{}", r.message);
				}
			}
		}
		// 根据战力排行
		playSides.sort(Comparator.comparing(a -> -a.snapshot.base.power));
		for (int i = 0; i < GDGuildWar.PT_MAX_NUM; i++) {
			int ptId = i + 1;
			GuildWarPt pt = field.b.getOrBuildPt(ptId);
			if (playSides.size() > i) {
				pt.ps = playSides.get(i);
			}
		}
	}

	private void noticeOnlineBase() {
		funcDependent.guild.noticeOnline(r -> {
			r.sendNow(new GuildWarBaseResp(getGame(), this));
		});
	}


	private void buildRobotPt() {
		if (pass()) {
			return;
		}
		GuildWarFightSide sideA = field.getSide(GDFight.Team.A);
		GuildWarFightSide sideB = field.getSide(GDFight.Team.B);
		buildRobotPt(sideA,sideB);
		buildRobotPt(sideB,sideA);
	}

	private void buildRobotPt(GuildWarFightSide side,GuildWarFightSide enemySide) {
		for (int i = 1; i <= GDGuildWar.PT_MAX_NUM; i++) {
			GuildWarPt pt = side.getOrBuildPt(i);
			if (pt.ps == null) {
				GuildWarPtRow row = getGame().table.guildWar.pt.findPt(enemySide.profile.level, i);
				if (row != null) {
					EcResult<PlaySide> p_r = getGame().fight.side.createPlaySideByCode(row.dynCfg);
					if (p_r.ok()) {
						pt.ps = p_r.data;
						pt.ps.snapshot.base.srvId = enemySide.profile.srvId;
					} else {
						log.error("构建公会战机器人实例异常{}", p_r.message);
					}
				}
			}
		}
	}

	@JsonIgnore
	private FightParam getFightParam(int difficulty) {
		FightParam param = new FightParam();
		GuildPO guild = funcDependent.guild;
		// 公会战废墟buff
		GuildWarBuffRow buffRow = getGame().table.guildWar.buff.get(guild.war.field.a.buffLevel);
		if (buffRow != null) {
			param.sideA.attrAppend.addTeamAttr(new FightAttr(buffRow.attrStr));
		}
		// 难度加成
		GuildWarDifficultyRow difficultyRow = getGame().table.guildWar.difficulty.get(difficulty);
		if (difficultyRow != null) {
			param.sideB.attrAppend.addTeamAttr(new FightAttr(difficultyRow.attrStr));
		}
		return param;
	}
}
